﻿
#ifndef __EFFEKSEERRENDERER_RING_RENDERER_BASE_H__
#define __EFFEKSEERRENDERER_RING_RENDERER_BASE_H__

//----------------------------------------------------------------------------------
// Include
//----------------------------------------------------------------------------------
#include <Effekseer.h>
#include <assert.h>
#include <math.h>
#include <string.h>

#include "EffekseerRenderer.CommonUtils.h"
#include "EffekseerRenderer.IndexBufferBase.h"
#include "EffekseerRenderer.RenderStateBase.h"
#include "EffekseerRenderer.StandardRenderer.h"
#include "EffekseerRenderer.VertexBufferBase.h"

#define __ZOFFSET__

//-----------------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------------
namespace EffekseerRenderer
{
//----------------------------------------------------------------------------------
//
//----------------------------------------------------------------------------------
typedef ::Effekseer::RingRenderer::NodeParameter efkRingNodeParam;
typedef ::Effekseer::RingRenderer::InstanceParameter efkRingInstanceParam;
typedef ::Effekseer::SIMD::Vec3f efkVector3D;

template <typename RENDERER, bool FLIP_RGB_FLAG>
class RingRendererBase : public ::Effekseer::RingRenderer, public ::Effekseer::SIMD::AlignedAllocationPolicy<16>
{
protected:
	struct KeyValue
	{
		float Key;
		efkRingInstanceParam Value;
	};
	std::vector<KeyValue> instances_;

	RENDERER* m_renderer;
	int32_t m_ringBufferOffset;
	uint8_t* m_ringBufferData;

	int32_t m_spriteCount;
	int32_t m_instanceCount;
	::Effekseer::SIMD::Mat44f m_singleRenderingMatrix;
	::Effekseer::RendererMaterialType materialType_ = ::Effekseer::RendererMaterialType::Default;

	int32_t vertexCount_ = 0;
	int32_t stride_ = 0;
	int32_t customData1Count_ = 0;
	int32_t customData2Count_ = 0;

public:
	RingRendererBase(RENDERER* renderer)
		: m_renderer(renderer)
		, m_ringBufferOffset(0)
		, m_ringBufferData(nullptr)
		, m_spriteCount(0)
		, m_instanceCount(0)
	{
	}

	virtual ~RingRendererBase()
	{
	}

protected:
	void RenderingInstance(const efkRingInstanceParam& instanceParameter,
						   const efkRingNodeParam& parameter,
						   const StandardRendererState& state,
						   const ::Effekseer::SIMD::Mat44f& camera)
	{
		const ShaderParameterCollector& collector = state.Collector;
		if (collector.ShaderType == RendererShaderType::Material)
		{
			Rendering_Internal<DynamicVertex, FLIP_RGB_FLAG>(parameter, instanceParameter, camera);
		}
		else if (collector.ShaderType == RendererShaderType::AdvancedLit)
		{
			Rendering_Internal<AdvancedLightingVertex, FLIP_RGB_FLAG>(parameter, instanceParameter, camera);
		}
		else if (collector.ShaderType == RendererShaderType::AdvancedBackDistortion)
		{
			Rendering_Internal<AdvancedLightingVertex, FLIP_RGB_FLAG>(parameter, instanceParameter, camera);
		}
		else if (collector.ShaderType == RendererShaderType::AdvancedUnlit)
		{
			Rendering_Internal<AdvancedSimpleVertex, FLIP_RGB_FLAG>(parameter, instanceParameter, camera);
		}
		else if (collector.ShaderType == RendererShaderType::Lit)
		{
			Rendering_Internal<LightingVertex, FLIP_RGB_FLAG>(parameter, instanceParameter, camera);
		}
		else if (collector.ShaderType == RendererShaderType::BackDistortion)
		{
			Rendering_Internal<LightingVertex, FLIP_RGB_FLAG>(parameter, instanceParameter, camera);
		}
		else
		{
			Rendering_Internal<SimpleVertex, FLIP_RGB_FLAG>(parameter, instanceParameter, camera);
		}
	}

	void BeginRendering_(RENDERER* renderer, int32_t count, const efkRingNodeParam& param, void* userData)
	{
		m_spriteCount = 0;
		const auto singleVertexCount = param.VertexCount * 8;
		const auto singleSpriteCount = param.VertexCount * 2;

		count = (std::min)(count, renderer->GetSquareMaxCount() / singleSpriteCount);

		m_instanceCount = count;

		instances_.clear();

		if (param.DepthParameterPtr->ZSort != Effekseer::ZSortType::None)
		{
			instances_.reserve(count);
		}

		EffekseerRenderer::StandardRendererState state;
		state.CullingType = ::Effekseer::CullingType::Double;
		state.DepthTest = param.ZTest;
		state.DepthWrite = param.ZWrite;

		state.Flipbook = ToState(param.BasicParameterPtr->Flipbook);

		state.UVDistortionIntensity = param.BasicParameterPtr->UVDistortionIntensity;

		state.TextureBlendType = param.BasicParameterPtr->TextureBlendType;

		state.BlendUVDistortionIntensity = param.BasicParameterPtr->BlendUVDistortionIntensity;

		state.EmissiveScaling = param.BasicParameterPtr->EmissiveScaling;

		state.EdgeThreshold = param.BasicParameterPtr->EdgeThreshold;
		state.EdgeColor[0] = param.BasicParameterPtr->EdgeColor[0];
		state.EdgeColor[1] = param.BasicParameterPtr->EdgeColor[1];
		state.EdgeColor[2] = param.BasicParameterPtr->EdgeColor[2];
		state.EdgeColor[3] = param.BasicParameterPtr->EdgeColor[3];
		state.EdgeColorScaling = param.BasicParameterPtr->EdgeColorScaling;
		state.IsAlphaCuttoffEnabled = param.BasicParameterPtr->IsAlphaCutoffEnabled;
		state.Maginification = param.Maginification;

		state.Distortion = param.BasicParameterPtr->MaterialType == Effekseer::RendererMaterialType::BackDistortion;
		state.DistortionIntensity = param.BasicParameterPtr->DistortionIntensity;
		state.MaterialType = param.BasicParameterPtr->MaterialType;

		state.RenderingUserData = param.UserData;
		state.HandleUserData = userData;

		state.LocalTime = param.LocalTime;

		state.CopyMaterialFromParameterToState(
			m_renderer,
			param.EffectPointer,
			param.BasicParameterPtr);

		customData1Count_ = state.CustomData1Count;
		customData2Count_ = state.CustomData2Count;

		materialType_ = param.BasicParameterPtr->MaterialType;

		renderer->GetStandardRenderer()->BeginRenderingAndRenderingIfRequired(state, count * singleVertexCount, stride_, (void*&)m_ringBufferData);

		vertexCount_ = count * singleVertexCount;
	}

	void Rendering_(const efkRingNodeParam& parameter,
					const efkRingInstanceParam& instanceParameter,
					const ::Effekseer::SIMD::Mat44f& camera)
	{
		if (parameter.DepthParameterPtr->ZSort == Effekseer::ZSortType::None)
		{
			const auto& state = m_renderer->GetStandardRenderer()->GetState();

			RenderingInstance(instanceParameter, parameter, state, camera);
		}
		else
		{
			if (instances_.size() >= m_instanceCount)
			{
				return;
			}

			KeyValue kv;
			kv.Value = instanceParameter;
			instances_.push_back(kv);
		}
	}

	template <typename VERTEX, bool FLIP_RGB>
	void Rendering_Internal(const efkRingNodeParam& parameter,
							const efkRingInstanceParam& instanceParameter,
							const ::Effekseer::SIMD::Mat44f& camera)
	{
		::Effekseer::SIMD::Mat43f mat43{};

		if (parameter.Billboard == ::Effekseer::BillboardType::Billboard ||
			parameter.Billboard == ::Effekseer::BillboardType::RotatedBillboard ||
			parameter.Billboard == ::Effekseer::BillboardType::YAxisFixed)
		{
			Effekseer::SIMD::Vec3f s;
			Effekseer::SIMD::Vec3f R;
			Effekseer::SIMD::Vec3f F;

			if (parameter.EnableViewOffset)
			{
				Effekseer::SIMD::Mat43f instMat = instanceParameter.SRTMatrix43;

				ApplyViewOffset(instMat, camera, instanceParameter.ViewOffsetDistance);

				CalcBillboard(parameter.Billboard, mat43, s, R, F, instMat, m_renderer->GetCameraFrontDirection());
			}
			else
			{
				CalcBillboard(parameter.Billboard, mat43, s, R, F, instanceParameter.SRTMatrix43, m_renderer->GetCameraFrontDirection());
			}

			ApplyDepthParameters(mat43,
								 m_renderer->GetCameraFrontDirection(),
								 m_renderer->GetCameraPosition(),
								 s,
								 parameter.DepthParameterPtr,
								 parameter.IsRightHand);

			mat43 = ::Effekseer::SIMD::Mat43f::Scaling(s) * mat43;
		}
		else if (parameter.Billboard == ::Effekseer::BillboardType::Fixed)
		{
			mat43 = instanceParameter.SRTMatrix43;

			if (parameter.EnableViewOffset)
			{
				ApplyViewOffset(mat43, camera, instanceParameter.ViewOffsetDistance);
			}

			ApplyDepthParameters(mat43,
								 m_renderer->GetCameraFrontDirection(),
								 m_renderer->GetCameraPosition(),
								 parameter.DepthParameterPtr,
								 parameter.IsRightHand);
		}

		int32_t singleVertexCount = parameter.VertexCount * 8;
		// Vertex* verteies = (Vertex*)m_renderer->GetVertexBuffer()->GetBufferDirect( sizeof(Vertex) * vertexCount );

		StrideView<VERTEX> verteies(m_ringBufferData, stride_, singleVertexCount);
		const float circleAngleDegree = (instanceParameter.ViewingAngleEnd - instanceParameter.ViewingAngleStart);
		const float stepAngleDegree = circleAngleDegree / (parameter.VertexCount);
		const float stepAngle = (stepAngleDegree) / 180.0f * 3.141592f;
		const float beginAngle = (instanceParameter.ViewingAngleStart + 90) / 180.0f * 3.141592f;

		const float outerRadius = instanceParameter.OuterLocation.GetX();
		const float innerRadius = instanceParameter.InnerLocation.GetX();
		const float centerRadius = innerRadius + (outerRadius - innerRadius) * instanceParameter.CenterRatio;

		const float outerHeight = instanceParameter.OuterLocation.GetY();
		const float innerHeight = instanceParameter.InnerLocation.GetY();
		const float centerHeight = innerHeight + (outerHeight - innerHeight) * instanceParameter.CenterRatio;

		::Effekseer::Color outerColor = instanceParameter.OuterColor;
		::Effekseer::Color innerColor = instanceParameter.InnerColor;
		::Effekseer::Color centerColor = instanceParameter.CenterColor;
		::Effekseer::Color outerColorNext = instanceParameter.OuterColor;
		::Effekseer::Color innerColorNext = instanceParameter.InnerColor;
		::Effekseer::Color centerColorNext = instanceParameter.CenterColor;

		if (parameter.StartingFade > 0)
		{
			outerColor.A = 0;
			innerColor.A = 0;
			centerColor.A = 0;
		}

		const float stepC = cosf(stepAngle);
		const float stepS = sinf(stepAngle);
		float cos_ = cosf(beginAngle);
		float sin_ = sinf(beginAngle);

		float uv0Current = instanceParameter.UV.X;
		const float uv0Step = instanceParameter.UV.Width / parameter.VertexCount;
		const float uv0v1 = instanceParameter.UV.Y;
		const float uv0v2 = uv0v1 + instanceParameter.UV.Height * 0.5f;
		const float uv0v3 = uv0v1 + instanceParameter.UV.Height;
		float uv0texNext = 0.0f;

		float uv1Current = 0.0f;
		const float uv1Step = 1.0f / parameter.VertexCount;
		const float uv1v1 = 0.0f;
		const float uv1v2 = uv1v1 + 0.5f;
		const float uv1v3 = uv1v1 + 1.0f;
		float uv1texNext = 0.0f;

		const int32_t advancedUVNum = 5;

		float advancedUVCurrent[advancedUVNum] =
			{
				instanceParameter.AlphaUV.X,
				instanceParameter.UVDistortionUV.X,
				instanceParameter.BlendUV.X,
				instanceParameter.BlendAlphaUV.X,
				instanceParameter.BlendUVDistortionUV.X};
		const float advancedUVStep[advancedUVNum] =
			{
				instanceParameter.AlphaUV.Width / parameter.VertexCount,
				instanceParameter.UVDistortionUV.Width / parameter.VertexCount,
				instanceParameter.BlendUV.Width / parameter.VertexCount,
				instanceParameter.BlendAlphaUV.Width / parameter.VertexCount,
				instanceParameter.BlendUVDistortionUV.Width / parameter.VertexCount};
		const float advancedUVv1[advancedUVNum] =
			{
				instanceParameter.AlphaUV.Y,
				instanceParameter.UVDistortionUV.Y,
				instanceParameter.BlendUV.Y,
				instanceParameter.BlendAlphaUV.Y,
				instanceParameter.BlendUVDistortionUV.Y};
		const float advancedUVv2[advancedUVNum] =
			{
				advancedUVv1[0] + instanceParameter.AlphaUV.Height * 0.5f,
				advancedUVv1[1] + instanceParameter.UVDistortionUV.Height * 0.5f,
				advancedUVv1[2] + instanceParameter.BlendUV.Height * 0.5f,
				advancedUVv1[3] + instanceParameter.BlendAlphaUV.Height * 0.5f,
				advancedUVv1[4] + instanceParameter.BlendUVDistortionUV.Height * 0.5f};
		const float advancedUVv3[advancedUVNum] =
			{
				advancedUVv1[0] + instanceParameter.AlphaUV.Height,
				advancedUVv1[1] + instanceParameter.UVDistortionUV.Height,
				advancedUVv1[2] + instanceParameter.BlendUV.Height,
				advancedUVv1[3] + instanceParameter.BlendAlphaUV.Height,
				advancedUVv1[4] + instanceParameter.BlendUVDistortionUV.Height};
		float advancedUVtexNext[advancedUVNum] = {0.0f};

		::Effekseer::SIMD::Vec3f outerNext, innerNext, centerNext;

		float currentAngleDegree = 0;
		float fadeStartAngle = parameter.StartingFade;
		float fadeEndingAngle = parameter.EndingFade;

		::Effekseer::SIMD::Vec3f outerCurrent(cos_ * outerRadius, sin_ * outerRadius, 0.0f);
		::Effekseer::SIMD::Vec3f innerCurrent(cos_ * innerRadius, sin_ * innerRadius, 0.0f);
		::Effekseer::SIMD::Vec3f centerCurrent(cos_ * centerRadius, sin_ * centerRadius, 0.0f);

		ZFixedTransformBlock outerTransform(mat43, outerHeight);
		ZFixedTransformBlock innerTransform(mat43, innerHeight);
		ZFixedTransformBlock centerTransform(mat43, centerHeight);

		outerTransform.Transform(outerCurrent);
		innerTransform.Transform(innerCurrent);
		centerTransform.Transform(centerCurrent);

		for (int i = 0; i < singleVertexCount; i += 8)
		{
			float old_c = cos_;
			float old_s = sin_;

			float t;
			t = cos_ * stepC - sin_ * stepS;
			sin_ = sin_ * stepC + cos_ * stepS;
			cos_ = t;

			outerNext = ::Effekseer::SIMD::Vec3f{cos_ * outerRadius, sin_ * outerRadius, 0};
			innerNext = ::Effekseer::SIMD::Vec3f{cos_ * innerRadius, sin_ * innerRadius, 0};
			centerNext = ::Effekseer::SIMD::Vec3f{cos_ * centerRadius, sin_ * centerRadius, 0};

			outerTransform.Transform(outerNext);
			innerTransform.Transform(innerNext);
			centerTransform.Transform(centerNext);

			currentAngleDegree += stepAngleDegree;

			// for floating decimal point error
			currentAngleDegree = Effekseer::Min(currentAngleDegree, circleAngleDegree);
			float alpha = 1.0f;
			if (currentAngleDegree < fadeStartAngle)
			{
				alpha = currentAngleDegree / fadeStartAngle;
			}
			else if (currentAngleDegree > circleAngleDegree - fadeEndingAngle)
			{
				alpha = 1.0f - (currentAngleDegree - (circleAngleDegree - fadeEndingAngle)) / fadeEndingAngle;
			}

			outerColorNext = instanceParameter.OuterColor;
			innerColorNext = instanceParameter.InnerColor;
			centerColorNext = instanceParameter.CenterColor;

			if (alpha != 1.0f)
			{
				outerColorNext.A = static_cast<uint8_t>(outerColorNext.A * alpha);
				innerColorNext.A = static_cast<uint8_t>(innerColorNext.A * alpha);
				centerColorNext.A = static_cast<uint8_t>(centerColorNext.A * alpha);
			}

			uv0texNext = uv0Current + uv0Step;

			StrideView<VERTEX> v(&verteies[i], stride_, 8);
			v[0].Pos = ToStruct(outerCurrent);
			v[0].SetColor(outerColor, FLIP_RGB);
			v[0].UV[0] = uv0Current;
			v[0].UV[1] = uv0v1;

			v[1].Pos = ToStruct(centerCurrent);
			v[1].SetColor(centerColor, FLIP_RGB);
			v[1].UV[0] = uv0Current;
			v[1].UV[1] = uv0v2;

			v[2].Pos = ToStruct(outerNext);
			v[2].SetColor(outerColorNext, FLIP_RGB);
			v[2].UV[0] = uv0texNext;
			v[2].UV[1] = uv0v1;

			v[3].Pos = ToStruct(centerNext);
			v[3].SetColor(centerColorNext, FLIP_RGB);
			v[3].UV[0] = uv0texNext;
			v[3].UV[1] = uv0v2;

			v[4] = v[1];

			v[5].Pos = ToStruct(innerCurrent);
			v[5].SetColor(innerColor, FLIP_RGB);
			v[5].UV[0] = uv0Current;
			v[5].UV[1] = uv0v3;

			v[6] = v[3];

			v[7].Pos = ToStruct(innerNext);
			v[7].SetColor(innerColorNext, FLIP_RGB);
			v[7].UV[0] = uv0texNext;
			v[7].UV[1] = uv0v3;

			for (int32_t uvi = 0; uvi < advancedUVNum; uvi++)
			{
				advancedUVtexNext[uvi] = advancedUVCurrent[uvi] + advancedUVStep[uvi];
			}

			SetVertexAlphaUV(v[0], advancedUVCurrent[0], 0);
			SetVertexAlphaUV(v[0], advancedUVv1[0], 1);

			SetVertexUVDistortionUV(v[0], advancedUVCurrent[1], 0);
			SetVertexUVDistortionUV(v[0], advancedUVv1[1], 1);

			SetVertexBlendUV(v[0], advancedUVCurrent[2], 0);
			SetVertexBlendUV(v[0], advancedUVv1[2], 1);

			SetVertexBlendAlphaUV(v[0], advancedUVCurrent[3], 0);
			SetVertexBlendAlphaUV(v[0], advancedUVv1[3], 1);

			SetVertexBlendUVDistortionUV(v[0], advancedUVCurrent[4], 0);
			SetVertexBlendUVDistortionUV(v[0], advancedUVv1[4], 1);

			SetVertexAlphaUV(v[1], advancedUVCurrent[0], 0);
			SetVertexAlphaUV(v[1], advancedUVv2[0], 1);

			SetVertexUVDistortionUV(v[1], advancedUVCurrent[1], 0);
			SetVertexUVDistortionUV(v[1], advancedUVv2[1], 1);

			SetVertexBlendUV(v[1], advancedUVCurrent[2], 0);
			SetVertexBlendUV(v[1], advancedUVv2[2], 1);

			SetVertexBlendAlphaUV(v[1], advancedUVCurrent[3], 0);
			SetVertexBlendAlphaUV(v[1], advancedUVv2[3], 1);

			SetVertexBlendUVDistortionUV(v[1], advancedUVCurrent[4], 0);
			SetVertexBlendUVDistortionUV(v[1], advancedUVv2[4], 1);

			SetVertexAlphaUV(v[2], advancedUVtexNext[0], 0);
			SetVertexAlphaUV(v[2], advancedUVv1[0], 1);

			SetVertexUVDistortionUV(v[2], advancedUVtexNext[1], 0);
			SetVertexUVDistortionUV(v[2], advancedUVv1[1], 1);

			SetVertexBlendUV(v[2], advancedUVtexNext[2], 0);
			SetVertexBlendUV(v[2], advancedUVv1[2], 1);

			SetVertexBlendAlphaUV(v[2], advancedUVtexNext[3], 0);
			SetVertexBlendAlphaUV(v[2], advancedUVv1[3], 1);

			SetVertexBlendUVDistortionUV(v[2], advancedUVtexNext[4], 0);
			SetVertexBlendUVDistortionUV(v[2], advancedUVv1[4], 1);

			SetVertexAlphaUV(v[3], advancedUVtexNext[0], 0);
			SetVertexAlphaUV(v[3], advancedUVv2[0], 1);

			SetVertexUVDistortionUV(v[3], advancedUVtexNext[1], 0);
			SetVertexUVDistortionUV(v[3], advancedUVv2[1], 1);

			SetVertexBlendUV(v[3], advancedUVtexNext[2], 0);
			SetVertexBlendUV(v[3], advancedUVv2[2], 1);

			SetVertexBlendAlphaUV(v[3], advancedUVtexNext[3], 0);
			SetVertexBlendAlphaUV(v[3], advancedUVv2[3], 1);

			SetVertexBlendUVDistortionUV(v[3], advancedUVtexNext[4], 0);
			SetVertexBlendUVDistortionUV(v[3], advancedUVv2[4], 1);

			v[4] = v[1];

			SetVertexAlphaUV(v[5], advancedUVCurrent[0], 0);
			SetVertexAlphaUV(v[5], advancedUVv3[0], 1);

			SetVertexUVDistortionUV(v[5], advancedUVCurrent[1], 0);
			SetVertexUVDistortionUV(v[5], advancedUVv3[1], 1);

			SetVertexBlendUV(v[5], advancedUVCurrent[2], 0);
			SetVertexBlendUV(v[5], advancedUVv3[2], 1);

			SetVertexBlendAlphaUV(v[5], advancedUVCurrent[3], 0);
			SetVertexBlendAlphaUV(v[5], advancedUVv3[3], 1);

			SetVertexBlendUVDistortionUV(v[5], advancedUVCurrent[4], 0);
			SetVertexBlendUVDistortionUV(v[5], advancedUVv3[4], 1);

			v[6] = v[3];

			SetVertexAlphaUV(v[7], advancedUVtexNext[0], 0);
			SetVertexAlphaUV(v[7], advancedUVv3[0], 1);

			SetVertexUVDistortionUV(v[7], advancedUVtexNext[1], 0);
			SetVertexUVDistortionUV(v[7], advancedUVv3[1], 1);

			SetVertexBlendUV(v[7], advancedUVtexNext[2], 0);
			SetVertexBlendUV(v[7], advancedUVv3[2], 1);

			SetVertexBlendAlphaUV(v[7], advancedUVtexNext[3], 0);
			SetVertexBlendAlphaUV(v[7], advancedUVv3[3], 1);

			SetVertexBlendUVDistortionUV(v[7], advancedUVtexNext[4], 0);
			SetVertexBlendUVDistortionUV(v[7], advancedUVv3[4], 1);

			for (int32_t vi = 0; vi < 8; vi++)
			{
				v[vi].SetFlipbookIndexAndNextRate(instanceParameter.FlipbookIndexAndNextRate);
				v[vi].SetAlphaThreshold(instanceParameter.AlphaThreshold);
			}

			if (VertexNormalRequired<VERTEX>())
			{
				StrideView<VERTEX> vs(&verteies[i], stride_, 8);

				// return back
				float t_b;
				t_b = old_c * (stepC)-old_s * (-stepS);
				auto s_b = old_s * (stepC) + old_c * (-stepS);
				auto c_b = t_b;

				::Effekseer::SIMD::Vec3f outerBefore{c_b * outerRadius, s_b * outerRadius, 0.0f};
				outerTransform.Transform(outerBefore);

				// next
				auto t_n = cos_ * stepC - sin_ * stepS;
				auto s_n = sin_ * stepC + cos_ * stepS;
				auto c_n = t_n;

				::Effekseer::SIMD::Vec3f outerNN{c_n * outerRadius, s_n * outerRadius, 0.0f};
				outerTransform.Transform(outerNN);

				::Effekseer::SIMD::Vec3f tangent0 = (outerCurrent - outerBefore).Normalize();
				::Effekseer::SIMD::Vec3f tangent1 = (outerNext - outerCurrent).Normalize();
				::Effekseer::SIMD::Vec3f tangent2 = (outerNN - outerNext).Normalize();

				auto tangentCurrent = (tangent0 + tangent1) / 2.0f;
				auto tangentNext = (tangent1 + tangent2) / 2.0f;

				auto binormalCurrent = v[5].Pos - v[0].Pos;
				auto binormalNext = v[7].Pos - v[2].Pos;

				::Effekseer::SIMD::Vec3f normalCurrent;
				::Effekseer::SIMD::Vec3f normalNext;

				normalCurrent = ::Effekseer::SIMD::Vec3f::Cross(tangentCurrent, binormalCurrent);
				normalNext = ::Effekseer::SIMD::Vec3f::Cross(tangentNext, binormalNext);

				if (!parameter.IsRightHand)
				{
					normalCurrent = -normalCurrent;
					normalNext = -normalNext;
				}

				normalCurrent = normalCurrent.Normalize();
				normalNext = normalNext.Normalize();

				tangentCurrent = tangentCurrent.Normalize();
				tangentNext = tangentNext.Normalize();

				const auto packedNormalCurrent = PackVector3DF(normalCurrent);
				const auto packedNormalNext = PackVector3DF(normalNext);
				const auto packedTangentCurrent = PackVector3DF(tangentCurrent);
				const auto packedTangentNext = PackVector3DF(tangentNext);

				vs[0].SetPackedNormal(packedNormalCurrent, FLIP_RGB);
				vs[1].SetPackedNormal(packedNormalCurrent, FLIP_RGB);
				vs[2].SetPackedNormal(packedNormalNext, FLIP_RGB);
				vs[3].SetPackedNormal(packedNormalNext, FLIP_RGB);

				vs[4].SetPackedNormal(packedNormalCurrent, FLIP_RGB);
				vs[5].SetPackedNormal(packedNormalCurrent, FLIP_RGB);
				vs[6].SetPackedNormal(packedNormalNext, FLIP_RGB);
				vs[7].SetPackedNormal(packedNormalNext, FLIP_RGB);

				vs[0].SetPackedTangent(packedTangentCurrent, FLIP_RGB);
				vs[1].SetPackedTangent(packedTangentCurrent, FLIP_RGB);
				vs[2].SetPackedTangent(packedTangentNext, FLIP_RGB);
				vs[3].SetPackedTangent(packedTangentNext, FLIP_RGB);

				vs[4].SetPackedTangent(packedTangentCurrent, FLIP_RGB);
				vs[5].SetPackedTangent(packedTangentCurrent, FLIP_RGB);
				vs[6].SetPackedTangent(packedTangentNext, FLIP_RGB);
				vs[7].SetPackedTangent(packedTangentNext, FLIP_RGB);

				// uv1
				uv1texNext = uv1Current + uv1Step;

				vs[0].SetUV2(uv1Current, uv1v1);
				vs[1].SetUV2(uv1Current, uv1v2);
				vs[2].SetUV2(uv1texNext, uv1v1);
				vs[3].SetUV2(uv1texNext, uv1v2);

				vs[4].SetUV2(uv1Current, uv1v2);

				vs[5].SetUV2(uv1Current, uv1v3);

				vs[6].SetUV2(uv1texNext, uv1v2);

				vs[7].SetUV2(uv1texNext, uv1v3);
			}

			outerCurrent = outerNext;
			innerCurrent = innerNext;
			centerCurrent = centerNext;
			uv0Current = uv0texNext;
			uv1Current = uv1texNext;
			for (int32_t uvi = 0; uvi < advancedUVNum; uvi++)
			{
				advancedUVCurrent[uvi] = advancedUVtexNext[uvi];
			}

			outerColor = outerColorNext;
			innerColor = innerColorNext;
			centerColor = centerColorNext;
		}

		// custom parameter
		if (customData1Count_ > 0)
		{
			StrideView<float> custom(m_ringBufferData + sizeof(DynamicVertex), stride_, singleVertexCount);
			for (int i = 0; i < singleVertexCount; i++)
			{
				auto c = (float*)(&custom[i]);
				memcpy(c, instanceParameter.CustomData1.data(), sizeof(float) * customData1Count_);
			}
		}

		if (customData2Count_ > 0)
		{
			StrideView<float> custom(
				m_ringBufferData + sizeof(DynamicVertex) + sizeof(float) * customData1Count_, stride_, singleVertexCount);
			for (int i = 0; i < singleVertexCount; i++)
			{
				auto c = (float*)(&custom[i]);
				memcpy(c, instanceParameter.CustomData2.data(), sizeof(float) * customData2Count_);
			}
		}

		m_spriteCount += 2 * parameter.VertexCount;
		m_ringBufferData += stride_ * singleVertexCount;
	}

	void EndRendering_(RENDERER* renderer, const efkRingNodeParam& param, const ::Effekseer::SIMD::Mat44f& camera)
	{
		if (param.DepthParameterPtr->ZSort != Effekseer::ZSortType::None)
		{
			for (auto& kv : instances_)
			{
				efkVector3D t = kv.Value.SRTMatrix43.GetTranslation();

				Effekseer::SIMD::Vec3f frontDirection = m_renderer->GetCameraFrontDirection();
				if (!param.IsRightHand)
				{
					frontDirection = -frontDirection;
				}

				kv.Key = Effekseer::SIMD::Vec3f::Dot(t, frontDirection);
			}

			if (param.DepthParameterPtr->ZSort == Effekseer::ZSortType::NormalOrder)
			{
				std::sort(instances_.begin(), instances_.end(), [](const KeyValue& a, const KeyValue& b) -> bool { return a.Key < b.Key; });
			}
			else
			{
				std::sort(instances_.begin(), instances_.end(), [](const KeyValue& a, const KeyValue& b) -> bool { return a.Key > b.Key; });
			}

			const auto& state = m_renderer->GetStandardRenderer()->GetState();

			for (auto& kv : instances_)
			{
				RenderingInstance(kv.Value, param, state, camera);
			}
		}
	}

public:
	void BeginRendering(const efkRingNodeParam& parameter, int32_t count, void* userData)
	{
		BeginRendering_(m_renderer, count, parameter, userData);
	}

	void Rendering(const efkRingNodeParam& parameter, const efkRingInstanceParam& instanceParameter, void* userData)
	{
		if (m_ringBufferData == nullptr)
			return;
		if (m_spriteCount + 2 * parameter.VertexCount > m_renderer->GetSquareMaxCount())
			return;
		Rendering_(parameter, instanceParameter, m_renderer->GetCameraMatrix());
	}

	void EndRendering(const efkRingNodeParam& parameter, void* userData)
	{
		if (m_ringBufferData == nullptr)
			return;
		if (m_spriteCount == 0 && parameter.DepthParameterPtr->ZSort == Effekseer::ZSortType::None)
			return;

		EndRendering_(m_renderer, parameter, m_renderer->GetCameraMatrix());
	}
};
//----------------------------------------------------------------------------------
//
//----------------------------------------------------------------------------------
} // namespace EffekseerRenderer
//----------------------------------------------------------------------------------
//
//----------------------------------------------------------------------------------
#endif // __EFFEKSEERRENDERER_RING_RENDERER_H__